package chapter

object Chapter14 extends App {

  // ex   Option   getOrElse 
  val stus = List("tom", "lilei", "lili")
  val scores = Map("tom" -> 10, "lili" -> 9)
  println(for (stu <- stus; score <- scores.get(stu)) yield (stu, score))

  //2
  def swap2(tup: (Int, Int)) = tup match {
    case (_1, _2) => (_2, _1)
  }
  assert((2, 1) == swap2((1, 2)))

  //3 
  def swap3(arys: Array[Int]) = arys match {
    case ary @ Array(elem1, elem2, other @ _*) => Array(elem2, elem1) ++ other
    case _ => arys
  }
  println(swap3(Array(1, 2, 3)))

  //4
  abstract class Item
  case class Article(description: String, price: Double) extends Item
  case class Bundle(description: String, discount: Double, items: Item*) extends Item
  case class Multiple(quantity: Int, item: Item) extends Item

  def price(item: Item): Double = item match {
    case Article(desc, price) => price
    case Bundle(_, discount, items @ _*) => items.map(price _).sum - discount
    case Multiple(quantity, item) => quantity * (price(item))
  }

  // 5
  def leafSum(leafNodes: List[Any]) = {
    def loop(sum: Int, list: List[Any]): Int =
      if (list.isEmpty) sum
      else
        loop(list.head match {
          case ni: Int => sum + ni
          case nl: List[_] => loop(0, nl)
          case _ => 0
        }, list.tail)
    loop(0, leafNodes)
  }
  println(leafSum(List(List(3, 5), 2, 8)))

  // 6 ,7  BinaryTree
  sealed abstract class BinaryTree
  case class Leaf(value: Int) extends BinaryTree
  case class Node(left: BinaryTree, right: BinaryTree) extends BinaryTree
  case class CNode(tree: BinaryTree*) extends BinaryTree
  def func67(tree: BinaryTree): Int = tree match {
    case Leaf(v) => v
    case Node(lv, rv) => func67(lv) + func67(rv)
    case CNode(trees @ _*) => trees.map(func67 _) reduceLeft (_ + _)
    case _ => 0
  }
  println(func67(Node(Node(Node(Leaf(3), Leaf(8)), Leaf(2)), Leaf(5))))

  //8 

  sealed abstract class OpTree
  case class Leaf2(value: Int) extends OpTree
  case class Node2(op: Char, leafs: OpTree*) extends OpTree

  def eval(tree: OpTree): Int = tree match {
    case Node2(op, leafs @ _*) => op match {
      case '+' => leafs.map(eval _).sum
      case '-' => -leafs.map(eval _).sum
      case '*' => leafs.map(eval _).product
    }
    case Leaf2(x) => x
  }

  println(eval(Node2('+', Node2('*', Leaf2(3), Leaf2(8)), Leaf2(2), Node2('-', Leaf2(5)))))

  //9
  def func9(list: List[Option[Int]]) = (for (opnum <- list; num <- opnum) yield num).sum

  //10
  type T = Double => Option[Double]
  def f(x: Double) = if (x >= 0) Some(Math.sqrt(x)) else None
  def g(x: Double) = if (x != 1) Some(1 / (x - 1)) else None

  def compose(f: T, g: T): T = {
    (d: Double) =>
      g(d) match {
        case Some(d) => f(d)
        case None => None
      }
  }

  val h1 = compose(f, g)

  def compose2(f: T, g: T): T = new Function1[Double, Option[Double]] {
    def isDefinedAt(v1: Double): Boolean = g(v1).isDefined && f(g(v1).get).isDefined
    def apply(v1: Double): Option[Double] = if (isDefinedAt(v1)) Some(f(g(v1).get).get) else None
  }
  val h2 = compose2(f, g)

  type R = (Double) => Option[Double]
  def compose3(f: R, g: R): R = g andThen { op =>  //same compose1
    op match {
      case Some(d) => f(d)
      case None => None
    }
  }

  val h3 = compose3(f, g)

  println("compose1", h1(0), h1(1), h1(2))
  println("compose2", h2(0), h2(1), h2(2))
  println("compose3", h3(0), h3(1), h3(2))
}